/*
 * Copyright (c) 2024 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <fstream>
#include <gtest/gtest.h>
#include <mc/dict.h>
#include <mc/filesystem.h>
#include <mc/log.h>
#include <memory>
#include <regex>
#include <sstream>
#include <string>
#include <vector>

// 测试用的日志追加器，将日志消息存储在内存中
class memory_appender : public mc::log::appender {
public:
    memory_appender() {
    }

    bool init(const mc::variant& args) override {
        return true;
    }

    void append(const mc::log::message& msg) override {
        m_messages.push_back(msg);
    }

    const std::vector<mc::log::message>& get_messages() const {
        return m_messages;
    }

    void clear() {
        m_messages.clear();
    }

private:
    std::vector<mc::log::message> m_messages;
};

// 日志框架测试类
class LogTest : public ::testing::Test {
protected:
    void SetUp() override {
        // 创建内存日志追加器
        m_memory_appender = std::make_shared<memory_appender>();

        // 创建测试日志器
        m_test_logger = mc::log::logger("test_logger");
        m_test_logger.set_level(mc::log::level::all);
        m_test_logger.add_appender(m_memory_appender);
    }

    void TearDown() override {
        m_test_logger.clear_appenders();
        m_memory_appender->clear();
    }

    bool message_contains(const mc::log::message& msg, const std::string& text) {
        return msg.get_message().find(text) != std::string::npos;
    }

    bool message_matches(const mc::log::message& msg, const std::string& pattern) {
        std::regex regex(pattern);
        return std::regex_search(msg.get_message(), regex);
    }

    std::string get_last_message() {
        const auto& messages = m_memory_appender->get_messages();
        return messages.empty() ? std::string{} : messages.back().get_message();
    }

    std::shared_ptr<memory_appender> m_memory_appender;
    mc::log::logger                  m_test_logger;
};

// 测试基本日志功能
TEST_F(LogTest, BasicLogging) {
    // 确保日志级别设置为允许所有级别的日志
    m_test_logger.set_level(mc::log::level::trace);

    // 使用结构化日志宏
    mc_ilog(m_test_logger, "这是一条信息日志");

    // 检查日志消息
    const auto& messages = m_memory_appender->get_messages();
    ASSERT_FALSE(messages.empty()) << "日志消息未被添加到内存追加器";
    auto msg = messages.back();
    EXPECT_EQ(mc::log::level::info, msg.get_level());
    EXPECT_TRUE(message_contains(msg, "这是一条信息日志"));

    // 清除之前的消息
    m_memory_appender->clear();

    // 使用不同级别的日志宏
    mc_tlog(m_test_logger, "跟踪日志");
    mc_dlog(m_test_logger, "调试日志");
    mc_wlog(m_test_logger, "警告日志");
    mc_nlog(m_test_logger, "通知日志");

    // 重新获取消息列表
    const auto& new_messages = m_memory_appender->get_messages();

    // 检查日志消息数量
    ASSERT_EQ(4, new_messages.size()) << "应该有4条日志消息被添加";

    // 检查最后一条消息
    msg = new_messages.back();
    EXPECT_EQ(mc::log::level::notice, msg.get_level());
    EXPECT_TRUE(message_contains(msg, "通知日志"));
}

// 测试结构化日志功能
TEST_F(LogTest, StructuredLogging) {
    // 确保日志级别设置为允许所有级别的日志
    m_test_logger.set_level(mc::log::level::trace);

    // 使用结构化日志宏
    mc_ilog(m_test_logger, "用户 ${user} 登录成功，IP: ${ip}",
            ("user", "admin")("ip", "192.168.1.1"));

    // 检查日志消息
    const auto& messages = m_memory_appender->get_messages();
    ASSERT_FALSE(messages.empty()) << "日志消息未被添加到内存追加器";
    auto msg = messages.back();
    EXPECT_EQ(mc::log::level::info, msg.get_level());
    EXPECT_TRUE(message_contains(msg, "用户 admin 登录成功，IP: 192.168.1.1"));

    // 检查参数字典
    const auto& args = msg.get_args();
    EXPECT_TRUE(args.contains("user"));
    EXPECT_TRUE(args.contains("ip"));
    EXPECT_EQ("admin", args["user"].as_string());
    EXPECT_EQ("192.168.1.1", args["ip"].as_string());

    // 清除之前的消息
    m_memory_appender->clear();

    // 使用多个参数
    mc_dlog(m_test_logger, "请求 ${method} ${url} 返回 ${status}",
            ("method", "GET")("url", "/api/users")("status", 200));

    // 检查日志消息
    const auto& new_messages = m_memory_appender->get_messages();
    ASSERT_EQ(1, new_messages.size()) << "应该有1条日志消息被添加";
    msg = new_messages.back();
    EXPECT_EQ(mc::log::level::debug, msg.get_level());
    EXPECT_TRUE(message_contains(msg, "请求 GET /api/users 返回 200"));

    // 检查参数字典
    const auto& args2 = msg.get_args();
    EXPECT_TRUE(args2.contains("method"));
    EXPECT_TRUE(args2.contains("url"));
    EXPECT_TRUE(args2.contains("status"));
}

// 测试日志级别过滤
TEST_F(LogTest, LevelFiltering) {
    // 设置日志级别为警告
    m_test_logger.set_level(mc::log::level::warn);

    // 发送不同级别的日志
    mc_tlog(m_test_logger, "跟踪日志"); // 不应该记录
    mc_dlog(m_test_logger, "调试日志"); // 不应该记录
    mc_ilog(m_test_logger, "信息日志"); // 不应该记录
    mc_wlog(m_test_logger, "警告日志"); // 应该记录
    mc_elog(m_test_logger, "错误日志"); // 应该记录

    // 检查日志消息
    auto& messages = m_memory_appender->get_messages();
    ASSERT_EQ(2, messages.size());

    // 检查第一条消息
    auto& msg1 = messages[0];
    EXPECT_EQ(mc::log::level::warn, msg1.get_level());
    EXPECT_TRUE(message_contains(msg1, "警告日志"));

    // 检查第二条消息
    auto& msg2 = messages[1];
    EXPECT_EQ(mc::log::level::error, msg2.get_level());
    EXPECT_TRUE(message_contains(msg2, "错误日志"));

    // 恢复日志级别
    m_test_logger.set_level(mc::log::level::trace);
}

// 测试日志上下文信息
TEST_F(LogTest, ContextInfo) {
    // 确保日志级别设置正确
    m_test_logger.set_level(mc::log::level::info);

    // 使用带上下文的日志宏
    mc_ilog(m_test_logger, "带有上下文的日志消息");

    // 检查日志消息
    auto& messages = m_memory_appender->get_messages();

    // 确保至少有一条消息被记录
    ASSERT_FALSE(messages.empty()) << "未捕获到日志消息，请检查日志级别设置";

    auto& msg = messages.back();

    // 检查上下文信息是否正确捕获
    const auto& ctx = msg.get_context();
    EXPECT_FALSE(ctx.m_file.empty());
    EXPECT_FALSE(ctx.m_function.empty());
    EXPECT_GT(ctx.m_line, 0);

    // 检查原始消息不包含上下文信息（因为使用延迟合并）
    EXPECT_EQ(msg.get_message().find(ctx.m_file), std::string::npos);
    EXPECT_EQ(msg.get_message().find(ctx.m_function), std::string::npos);
}

// 测试全局日志宏
TEST_F(LogTest, GlobalLogMacros) {
    // 创建一个新的日志记录器，而不是使用默认日志记录器
    auto test_global_logger = mc::log::log_manager::instance().get_logger("test_global");

    // 添加内存追加器
    auto mem_appender = std::make_shared<memory_appender>();
    test_global_logger.add_appender(mem_appender);

    // 设置日志级别
    test_global_logger.set_level(mc::log::level::trace);

    // 使用指定日志记录器的日志宏
    mc_ilog(test_global_logger, "这是一条全局信息日志");
    mc_dlog(test_global_logger, "这是一条全局调试日志");
    mc_wlog(test_global_logger, "这是一条全局警告日志");

    // 使用结构化日志宏
    mc_ilog(test_global_logger, "用户 ${user} 登录", ("user", "guest"));

    // 检查日志消息
    auto& messages = mem_appender->get_messages();
    ASSERT_EQ(4, messages.size());

    // 检查最后一条消息
    auto& last_msg = messages.back();
    EXPECT_EQ(mc::log::level::info, last_msg.get_level());
    EXPECT_TRUE(message_contains(last_msg, "用户 guest 登录"));
}

// 测试复杂数据类型日志
TEST_F(LogTest, ComplexDataLogging) {
    // 创建复杂数据
    mc::dict user_info = mc::dict({{"id", 12345},
                                   {"name", "张三"},
                                   {"roles", mc::variants{"admin", "user"}},
                                   {"active", true}});

    // 记录包含复杂数据的日志
    mc_ilog(m_test_logger, "用户信息: ${info}", ("info", user_info));

    // 检查日志消息
    auto& messages = m_memory_appender->get_messages();
    ASSERT_FALSE(messages.empty());
    auto& msg = messages.back();
    EXPECT_EQ(mc::log::level::info, msg.get_level());
    EXPECT_TRUE(message_contains(msg, "用户信息:"));

    // 检查参数字典
    const auto& args = msg.get_args();
    EXPECT_TRUE(args.contains("info"));
}

/*
早期日志只支持 ${} 命名参数，为了使用简单我们将日志格式化与 sformat 保持一致，
即支持 {} 占位也支持 ${} 命名占位符，其中 {} 既可以索引占位也可以命名占位，${} 占位
只是兼容旧的写法，建议的日志写法全部是 {} 占位符
*/
TEST_F(LogTest, basic_smart_log) {
    // 测试自增索引 {}
    mc_ilog(m_test_logger, "{} {}", "Hello", "World");
    EXPECT_EQ(get_last_message(), "Hello World");

    // 测试显式索引 {0}, {1}
    mc_ilog(m_test_logger, "{1} {0}", "World", "Hello");
    EXPECT_EQ(get_last_message(), "Hello World");

    // 测试命名参数 {name}
    mc_ilog(m_test_logger, "{name} {value}", ("name", "Answer"), ("value", 42));
    EXPECT_EQ(get_last_message(), "Answer 42");
}

// 测试混合使用不同类型的占位符
TEST_F(LogTest, mixed_placeholder_types) {
    // 混合自增索引和命名参数
    mc_ilog(m_test_logger, "{} is {age} years old", "Alice", ("age", 25));
    EXPECT_EQ(get_last_message(), "Alice is 25 years old");

    // 混合显式索引和命名参数
    mc_ilog(m_test_logger, "{0} has {balance:.2f} dollars", "Bob", ("balance", 123.456));
    EXPECT_EQ(get_last_message(), "Bob has 123.46 dollars");

    // 混合所有类型
    mc_ilog(m_test_logger, "{} {name} {2}", "Hello", ("name", "Test"), "World"); // 命名参数也会增加索引
    EXPECT_EQ(get_last_message(), "Hello Test World");
}

// 测试带格式说明符的智能占位符
TEST_F(LogTest, format_specifications) {
    // 自增索引带格式
    mc_ilog(m_test_logger, "{:.2f} {}", 3.14159, "pi");
    EXPECT_EQ(get_last_message(), "3.14 pi");

    // 显式索引带格式
    mc_ilog(m_test_logger, "{0:.2f} {1:>10}", 3.14159, "right");
    EXPECT_EQ(get_last_message(), "3.14      right");

    // 命名参数带格式
    mc_ilog(m_test_logger, "{name:<10} {value:04d}", ("name", "Number"), ("value", 42));
    EXPECT_EQ(get_last_message(), "Number     0042");
}

// 测试动态格式参数
TEST_F(LogTest, dynamic_format_parameters) {
    // 使用命名参数作为动态宽度和精度
    mc_ilog(m_test_logger, "{value:{width}.{precision}f}",
            ("value", 3.14159),
            ("width", 10),
            ("precision", 3));
    EXPECT_EQ(get_last_message(), "     3.142");

    // 混合索引和命名参数的动态格式
    mc_ilog(m_test_logger, "{0:{width}.2f}", 3.14159, ("width", 8));
    EXPECT_EQ(get_last_message(), "    3.14");
}

// 测试边界情况
TEST_F(LogTest, edge_cases) {
    // 单个字符的命名参数
    mc_ilog(m_test_logger, "{x} {y}", ("x", 10), ("y", 20));
    EXPECT_EQ(get_last_message(), "10 20");

    // 长命名参数
    mc_ilog(m_test_logger, "{very_long_parameter_name}", ("very_long_parameter_name", "value"));
    EXPECT_EQ(get_last_message(), "value");

    // 提供了3个参数但格式化字符串只用了一部分，正常编译期报错，这里使用不安全版本
    mc_ilog_unsafe(m_test_logger, "{2}", "first", "second", "target_value");
    EXPECT_EQ(get_last_message(), "target_value");

    // 格式化字符串不是字面值常量而是变量，只能使用不安全版本
    const char* fmt = "{x} {y}";
    mc_ilog_unsafe(m_test_logger, fmt, ("x", 10), ("y", 20));
    EXPECT_EQ(get_last_message(), "10 20");
}

// 测试嵌套大括号
TEST_F(LogTest, nested_braces) {
    // 命名参数中的嵌套大括号格式
    mc_ilog(m_test_logger, "{value:{width}.{precision}f}",
            ("value", 123.456),
            ("width", 12),
            ("precision", 2));
    EXPECT_EQ(get_last_message(), "      123.46");

    // 索引参数中的嵌套大括号格式
    mc_ilog(m_test_logger, "{0:{1}.{2}f}", 123.456, 12, 2);
    EXPECT_EQ(get_last_message(), "      123.46");
}

// 测试转义字符
TEST_F(LogTest, escaped_braces) {
    // 转义的大括号与智能占位符混合
    mc_ilog(m_test_logger, "{{}} {name} {{}}", ("name", "test"));
    EXPECT_EQ(get_last_message(), "{} test {}");

    mc_ilog(m_test_logger, "{{0}} {0} {{name}}", "value");
    EXPECT_EQ(get_last_message(), "{0} value {name}");
}
